
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System; 
using System.Collections;
using System.Diagnostics;
using System.Xml;



namespace Swaf.Gui
{
	/// <summary>
	/// Summary description for ActionCommand.
	/// </summary>
	[Serializable]
	public class Command : ICommand
	{
		protected enum CommandType {basicCommand, enumCommand, seperatorCommand};

		protected CommandType m_commandType;
		protected IList m_childCommands;
		protected IDictionary m_commandLooks;

		protected ICommand m_parentCommand;
		protected object m_commandParam;

		protected string m_commandState="";

		protected bool m_enabled = true;

		protected IList m_highlightTriggers = new ArrayList();
		protected IList m_selectTriggers = new ArrayList();

		public Command(IView view, XmlElement initInfo)
		{
			if(initInfo.Name.Equals("Command"))
			{
				m_childCommands = new ArrayList();
				m_commandLooks = new Hashtable();

				m_commandLooks[buildLookKey("","")] = buildLook(initInfo);
				XmlAttribute attr = initInfo.GetAttributeNode("forProcessAction");
				if(attr != null)
					attachToProcessAction(view, attr.Value);

				processChildCommands(view, initInfo);
			}
			else if(initInfo.Name.Equals("Seperator"))
			{
				m_commandType = CommandType.seperatorCommand;
			}
			
		}

		public Command(Command parent, IAction action, string paramOption, IDictionary looks)
		{
			m_commandLooks = looks;
			m_parentCommand = parent;
			m_commandParam = paramOption;
			m_enabled = action.Enabled;
			m_commandType = CommandType.basicCommand;
			registerTrigger(CommandMode.Selected, action, "trigger");
		}

		public Command(Command parent, IAction action)
		{
			m_commandLooks = new Hashtable();
			m_commandLooks[buildLookKey("","")] = new CommandLook(action.ShortName, "");
			m_parentCommand = parent;
			setupForSingleAction(action);
			//registerTrigger(CommandMode.Selected, action, "trigger");
		}

		public event CommandChanged OnChange;

		public bool UseChildenAsStandins {get{return m_commandType == CommandType.enumCommand;}}
		public bool IsSeperator{get{return m_commandType == CommandType.seperatorCommand;}}

		public void registerTrigger(CommandMode mode, object callbackObj, string methodName)
		{
			switch(mode)
			{
				case CommandMode.Highlighted:
					m_highlightTriggers.Add(new DynamicCall(callbackObj, methodName));
					break;
				case CommandMode.Selected:
					m_selectTriggers.Add(new DynamicCall(callbackObj, methodName));
					break;
				default:
					throw new Exception(String.Format("Invalid CommandMode option: {0}", mode.ToString()));
					break;
			}
		}

		public IList Children
		{
			get
			{
				return m_childCommands;
			}
		}

		public string getText(CommandMode mode)
		{
			string t = "";

			CommandLook look = findLook(mode);
			if(look != null)
				t = m_commandParam==null?look.Text:String.Format(look.Text, m_commandParam);

			return t;
		}

		/// <summary>The location of the image to display to the user for the command.</summary>
		public Uri getImageUrl(CommandMode mode)
		{
			Uri u = null;

			CommandLook look = findLook(mode);
			if(look != null)
				u = new Uri(look.ImageUrl);

			return u;
		}

		public bool Enabled
		{
			get
			{
				return m_enabled;
			}
			set
			{
				m_enabled = value;
				OnChange(this, CommandChangeType.Enabled);
			}
		}

		public void triggerSelection()
		{
			foreach(DynamicCall call in m_selectTriggers)
				attemptCallback(call);
		}

		public void triggerHighlight()
		{
			foreach(DynamicCall call in m_highlightTriggers)
				attemptCallback(call);
		}

		protected void attemptCallback(DynamicCall call)
		{
			bool called = false;
			try
			{
				if(m_commandParam != null)
				{
					call.call(m_commandParam);
					called = true;
				}
			}
			catch(Exception)
			{
				//TODO: Log a problem with a callback on the command trigger.
				//Not a big problem as an attempt to call the object without the command
				//param will be made below.
			}
			if(!called)
				try
				{
					call.call();
				}
				catch(Exception)
				{
					//TODO: Log a problem with a callback on the command trigger.
				}
		}

		protected string buildLookKey(string mode, string state)
		{
			return String.Format("{0}.{1}", mode, state);
		}

		protected CommandLook buildLook(XmlElement info)
		{
			return new CommandLook(info.GetAttribute("text"), info.GetAttribute("imageUrl"));
		}

		protected CommandLook findLook(CommandMode mode)
		{
			//This array of keys acts as a search path for to find a look.  
			//Given the way commands should be built, the default look
			//should always be found as that look should always be defined.
			string[] lookKeys = new string[]{
				buildLookKey(mode.ToString(), m_commandState), //Look specific to the given mode and state
				buildLookKey("", m_commandState), //Look specific to the given state
				buildLookKey("","") //Default look regardless of mode or state.
			};

			foreach(string key in lookKeys)
				if(m_commandLooks.Contains(key))
					return (CommandLook)m_commandLooks[key];

			Debug.Assert(false, "Should never reach this point... Right?");
			return null;
		}


		protected void attachToProcessAction(IView view, string actionName)
		{
			string processName = NameAddress.getFirstAddress(actionName);
			IBusinessProcess process = (IBusinessProcess)view.namedElement(processName);
			if(process == null)
				throw new Exception(String.Format("Invalid business process specifid for command: {0}", actionName));

			if(NameAddress.isSingleAddress(actionName))
			{
				m_childCommands = new ArrayList();
				m_commandType = CommandType.enumCommand;

				//Iterate through all actions in the bp
				foreach(IAction action in process.Actions.Values)
				{
					Command childCommand = new Command(this, action);
					m_childCommands.Add(childCommand);
				}
			}
			else
			{
				setupForSingleAction((IAction)process.Actions[NameAddress.getRemainingAddress(actionName)]);
			}
				

		}

		protected void setupForSingleAction(IAction action)
		{
			Debug.Assert(action != null);
			m_enabled = action.Enabled;

			if(action.ValidParameterOptions.Count > 0)
				setupFromEnumeratedAction(action);
			else
				setupFromSimpleAction(action);

			action.ActionChanged +=new ActionChanged(onActionChanged);
		}

		protected void setupFromEnumeratedAction(IAction action)
		{
			m_childCommands = new ArrayList();
			m_commandType = CommandType.enumCommand;

			foreach(string paramOption in action.ValidParameterOptions)
			{
				Command childCommand = new Command(this, action, paramOption, m_commandLooks);
				m_childCommands.Add(childCommand);
			}
		}

		protected void setupFromSimpleAction(IAction action)
		{
			m_commandType = CommandType.basicCommand;
			this.registerTrigger(CommandMode.Selected, action, "trigger");
		}

		protected void processChildCommands(IView view, XmlElement initInfo)
		{
			foreach(XmlElement childInfo in initInfo.ChildNodes)
			{
				if(childInfo.Name.Equals("Command"))
				{
					m_childCommands.Add(new Command(view, childInfo));
				}
				else if(childInfo.Name.Equals("State"))
				{
					m_commandLooks[buildLookKey("", childInfo.GetAttribute("name"))] = 
						new CommandLook(childInfo.GetAttribute("text"), childInfo.GetAttribute("image"));
				}
			}
		}

		private void onActionChanged(object sender, ActionChangeType type)
		{
			if(type == ActionChangeType.Enabled)
				Enabled = ((IAction)sender).Enabled;
			else if(type == ActionChangeType.ParameterOptions && m_commandType == CommandType.enumCommand)
			{
				setupFromEnumeratedAction((IAction)sender);
				OnChange(this, CommandChangeType.DisplayInfo);
			}
		}

		protected class CommandLook
		{
			protected string m_text;
			protected string m_imageUrl;

			public CommandLook(string text, string imageUrl)
			{
				m_text = text;
				m_imageUrl = imageUrl;
			}

			public string Text{get{return m_text;}}
			public string ImageUrl{get{return m_imageUrl;}}
		}

	}
}
